package io.github.heollhai.common.config.reids;

import com.alibaba.fastjson.JSON;
import io.github.heollhai.common.utils.DateUtil;
import io.github.heollhai.common.utils.VerifyUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author: lmf
 * @Create: 2024/7/8 10:39
 * Description: redis 工具类
 */
@Component
@Slf4j
public class RedisOperator {
    /**
     * 分隔符
     */
    public final String SPLIT = "&";
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // ===============================判断值==start======================================================

    /**
     * 判断key是否存在
     */
    public boolean keyIsExist(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

    // ===============================判断值==end======================================================


    // ===============================设置值==start======================================================

    /**
     * 设置值，并添加过期时间
     */
    public void set(String key, Object value, long timeout) {
        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置 hash类型值
     */
    public void setHashValue(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }




    /**
     * 设置值，并在值上添加时间戳
     */
    public void setValueAddTimestamp(String key, String value, Integer type, long timeout) {
        // 值 + 时间戳 + 使用次数
        String str = value + SPLIT + DateUtil.getTimestamp() + SPLIT + type + SPLIT + 0;
        redisTemplate.opsForValue().set(key, str, timeout, TimeUnit.SECONDS);
    }

    /**
     * 实现命令：expire 设置过期时间，单位秒
     */
    public void expire(String key, long timeout) {
        redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 实现命令：setnx，设置键值对，如果键不存在，则设置键值对，如果键存在，则不设置键值对
     */
    public Boolean setIfAbsent(String key, String value, long timeout) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
    }


    /**
     * 根据给定的键值对执行特定的Lua脚本。
     * 该方法的目的是检查给定键的当前值是否与指定值相等，如果相等，则删除该键。
     * 使用Lua脚本的原因是，它提供了一种原子性操作的方式，确保了操作的完整性。
     * @return 返回一个整数，表示操作的结果。如果键的值与指定值相等且键被成功删除，则返回1；否则返回0。
     */
    public  Integer execute(List<String> keys, Object... args) {
        // 定义Lua脚本，该脚本检查键的值是否与给定的值相等，如果相等，则删除该键。
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        return redisTemplate.execute(new DefaultRedisScript<>(script,Integer.class) , keys,args);
    }


    // ===============================设置值==end======================================================


    // ===============================修改值==start======================================================

    /**
     * 将对应时间戳的值的使用次数+1
     */
    public void addValueUseCount(String key) {
        // 根据键获取值
        String value = get(key);
        // 检查值是否为空，如果为空则直接返回，不进行后续处理
        if (VerifyUtils.isEmpty(value)) {
            return;
        }
        // 检查值是否包含分割字符，如果包含，则进一步处理
        if (value.contains(SPLIT)) {
            // 使用分割字符将值分割成数组
            String[] split = value.split(SPLIT);
            // 检查分割后的数组长度是否为3，如果不是，则不进行后续处理
            if (split.length == 4) {
                // 提取使用次数部分并转换为整数
                int useCount = Integer.parseInt(split[2]);
                // 使用次数加1
                useCount++;
                // 重新组合值，包括原始部分、时间戳和更新后的使用次数
                String str = split[0] + SPLIT + split[1] + SPLIT + split[2] + SPLIT + useCount;
                redisTemplate.opsForValue().set(key, str);
            }
        }

    }

    /**
     * 在列表的最右边塞入一个value
     *
     * @param key 键
     * @param value 值
     */
    public <T> void rPush(String key, T value) {
        redisTemplate.opsForList().rightPush(key, value);
    }


    /**
     * 修改列表指定下标的值
     */
    public <T> void setIndex(String key, int index, T value) {
        redisTemplate.opsForList().set(key, index, value);
    }
    // ===============================修改值==emd======================================================

    // ===============================获取值==start======================================================

    /**
     * 实现命令：GET key，返回 key所关联的字符串值。
     */
    public String get(String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }

    /**
     * 获取指定索引位置的值, index为-1时，表示返回的是最后一个；当index大于实际的列表长度时，返回null
     *
     * @param key key
     * @param index 下标
     */
    public Object indexValue(String key, int index) {
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * 获取值 并解析成指定类
     */
    public <T> T get(String key, Class<T> clazz) {
        // 假设 redisTemplate 已经配置为使用 FastJson2JsonRedisSerializer 或其他 JSON 序列化器
        ValueOperations<String,Object> stringValueOperations = redisTemplate.opsForValue(); // 注意这里假设存储的是 String 类型的 JSON
        String o = (String) stringValueOperations.get(key);
        if(o == null){
            return null;
        }
        return JSON.parseObject(o, clazz);
    }

    /**
     * 获取redis中hash类型值
     */
    public String getHashValue(String key, String hashKey) {
        return (String) redisTemplate.opsForHash().get(key, hashKey);
    }

    /**
     * 获取redis中 hash类型值 并解析成指定类
     */
    public <T> T getHashValue(String key, String hashKey, Class<T> clazz) {
        // 假设 redisTemplate 已经配置为使用 FastJson2JsonRedisSerializer 或其他 JSON 序列化器
        Object o = redisTemplate.opsForHash().get(key, hashKey);// 注意这里假设存储的是 String 类型的
        // JSON
        if(o == null){
            return null;
        }
        return JSON.parseObject(o.toString(),clazz);
    }


    /**
     * 获取值 hash 类型值
     */
    public BoundHashOperations<String, Object, Object> getBoundHashOperations(String key) {
       return redisTemplate.boundHashOps(key);
    }
    // ===============================获取值==end======================================================

    // ===============================删除值==start======================================================

    /**
     * 删除key
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }
    /**
     * 删除hash key
     */
    public void deleteHashKey(String key, String hashKey) {
        redisTemplate.opsForHash().delete(key, hashKey);
    }
    /**
     * 删除 hash 中所有的key
     */
    public void deleteHashAllKey(String key) {
        redisTemplate.opsForHash().delete(key);
    }

    /**
     * 删除列表中值为value的元素，总共删除count次；
     * <p>
     * 如原来列表为 【1， 2， 3， 4， 5， 2， 1， 2， 5】
     * 传入参数 value=2, count=1 表示删除一个列表中value为2的元素
     * 则执行后，列表为 【1， 3， 4， 5， 2， 1， 2， 5】
     *
     * @param key key
     * @param value 值
     * @param count 删除次数
     */
    public Boolean remove(String key, String value, int count) {
        try {
            redisTemplate.opsForList().remove(key, count, value);
            return true;
        } catch (Exception e) {
            log.error("删除列表的元素值失败！|参数,key:{},value:{},count:{}", key, value, count);
            return false;
        }
    }
    // ===============================删除值==end======================================================
}
